﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TableDependency.Abstracts;
using TableDependency.Enums;
using TableDependency.EventArgs;

namespace TableDependency.SqlClient.Where.IntegrationTests
{
    public class TableDependencyFilterSqlServerModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class CustomSqlTableDependencyFilter : ITableDependencyFilter
    {
        private readonly int _id;

        public CustomSqlTableDependencyFilter(int id)
        {
            _id = id;
        }

        public string Translate()
        {
            return "[Id] = " + _id;
        }
    }

    [TestClass]
    public class IntegrationTestITableDependencyFilter
    {
        private static readonly string ConnectionString = ConfigurationManager.ConnectionStrings["SqlServerConnectionString"].ConnectionString;
        private const string TableName = "TableDependencyFilterSqlServerModel";
        private static int _counter;
        private static readonly Dictionary<string, Tuple<EqualToTestSqlServerModel, EqualToTestSqlServerModel>> CheckValues = new Dictionary<string, Tuple<EqualToTestSqlServerModel, EqualToTestSqlServerModel>>();

        [ClassInitialize()]
        public static void ClassInitialize(TestContext testContext)
        {
            using (var sqlConnection = new SqlConnection(ConnectionString))
            {
                sqlConnection.Open();
                using (var sqlCommand = sqlConnection.CreateCommand())
                {
                    sqlCommand.CommandText = $"IF OBJECT_ID('{TableName}', 'U') IS NOT NULL DROP TABLE [{TableName}];";
                    sqlCommand.ExecuteNonQuery();

                    sqlCommand.CommandText =
                        $"CREATE TABLE [{TableName}]( " +
                        "[Id] [int] NOT NULL, " +
                        "[Name] [nvarchar](50) NOT NULL, " +
                        "[Second Name] [nvarchar](50) NULL, " +
                        "[Born] [datetime] NULL)";
                    sqlCommand.ExecuteNonQuery();
                }
            }
        }

        [ClassCleanup()]
        public static void ClassCleanup()
        {
            using (var sqlConnection = new SqlConnection(ConnectionString))
            {
                sqlConnection.Open();
                using (var sqlCommand = sqlConnection.CreateCommand())
                {
                    sqlCommand.CommandText = $"IF OBJECT_ID('{TableName}', 'U') IS NOT NULL DROP TABLE [{TableName}];";
                    sqlCommand.ExecuteNonQuery();
                }
            }
        }

        [TestMethod]
        public void CistomITableDependencyFilterTest1()
        {
            SqlTableDependency<TableDependencyFilterSqlServerModel> tableDependency = null;

            ITableDependencyFilter filterExpression = new CustomSqlTableDependencyFilter(2);

            try
            {
                tableDependency = new SqlTableDependency<TableDependencyFilterSqlServerModel>(ConnectionString, filter: filterExpression);
                tableDependency.OnChanged += TableDependency_Changed;
                tableDependency.Start();

                Thread.Sleep(5000);

                var t = new Task(ModifyTableContent);
                t.Start();
                t.Wait(20000);
            }
            finally
            {
                tableDependency?.Dispose();
            }

            Assert.AreEqual(_counter, 3);
            Assert.AreEqual(CheckValues[ChangeType.Insert.ToString()].Item2.Name, CheckValues[ChangeType.Insert.ToString()].Item1.Name);
            Assert.AreEqual(CheckValues[ChangeType.Update.ToString()].Item2.Name, CheckValues[ChangeType.Update.ToString()].Item1.Name);
            Assert.AreEqual(CheckValues[ChangeType.Delete.ToString()].Item2.Name, CheckValues[ChangeType.Delete.ToString()].Item1.Name);
        }

        private static void TableDependency_Changed(object sender, RecordChangedEventArgs<TableDependencyFilterSqlServerModel> e)
        {
            _counter++;

            switch (e.ChangeType)
            {
                case ChangeType.Insert:
                    CheckValues[ChangeType.Insert.ToString()].Item2.Name = e.Entity.Name;
                    break;
                case ChangeType.Update:
                    CheckValues[ChangeType.Update.ToString()].Item2.Name = e.Entity.Name;
                    break;
                case ChangeType.Delete:
                    CheckValues[ChangeType.Delete.ToString()].Item2.Name = e.Entity.Name;
                    break;
            }
        }

        private static void ModifyTableContent()
        {
            CheckValues.Add(ChangeType.Insert.ToString(), new Tuple<EqualToTestSqlServerModel, EqualToTestSqlServerModel>(new EqualToTestSqlServerModel { Name = "Christian" }, new EqualToTestSqlServerModel()));
            CheckValues.Add(ChangeType.Update.ToString(), new Tuple<EqualToTestSqlServerModel, EqualToTestSqlServerModel>(new EqualToTestSqlServerModel { Name = "Velia" }, new EqualToTestSqlServerModel()));
            CheckValues.Add(ChangeType.Delete.ToString(), new Tuple<EqualToTestSqlServerModel, EqualToTestSqlServerModel>(new EqualToTestSqlServerModel { Name = "Velia" }, new EqualToTestSqlServerModel()));

            using (var sqlConnection = new SqlConnection(ConnectionString))
            {
                sqlConnection.Open();
                using (var sqlCommand = sqlConnection.CreateCommand())
                {
                    sqlCommand.CommandText = $"INSERT INTO [{TableName}] ([Id], [Name]) VALUES (1, 'Valentina')";
                    sqlCommand.ExecuteNonQuery();
                    Thread.Sleep(500);

                    sqlCommand.CommandText = $"INSERT INTO [{TableName}] ([Id], [Name]) VALUES (2, '{CheckValues[ChangeType.Insert.ToString()].Item1.Name}')";
                    sqlCommand.ExecuteNonQuery();
                    Thread.Sleep(500);

                    sqlCommand.CommandText = $"UPDATE [{TableName}] SET [Name] = 'Aurelia' WHERE Id = 1";
                    sqlCommand.ExecuteNonQuery();
                    Thread.Sleep(500);

                    sqlCommand.CommandText = $"UPDATE [{TableName}] SET [Name] = '{CheckValues[ChangeType.Update.ToString()].Item1.Name}' WHERE Id = 2";
                    sqlCommand.ExecuteNonQuery();
                    Thread.Sleep(500);

                    sqlCommand.CommandText = $"DELETE FROM [{TableName}] WHERE Id = 1";
                    sqlCommand.ExecuteNonQuery();
                    Thread.Sleep(500);

                    sqlCommand.CommandText = $"DELETE FROM [{TableName}] WHERE Id = 2";
                    sqlCommand.ExecuteNonQuery();
                    Thread.Sleep(500);
                }
            }
        }
    }
}